﻿using System;
using System.Collections.Generic;
using GDIP = System.Drawing;
using System.Text;

namespace iNet.Media
{
	/// <summary>
	/// 提供內容配置相關方法。
	/// </summary>
	public static class LayoutUtility
	{
		#region GetContentBounds
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">指定的內容大小，若內容大小超過指定的矩形範圍則等比例縮小。</param>
		/// <param name="align">內容在指定的矩形範圍中的對齊方式。</param>
		/// <returns>指定內容在指定範圍中的位置及大小。</returns>
		public static GDIP.Rectangle GetContentBounds(GDIP.Rectangle bounds, GDIP.Size contentSize, GDIP.ContentAlignment align)
		{
			return GetContentBounds(bounds, contentSize, true, align);
		}
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">指定的內容大小。</param>
		/// <param name="fitToBounds">若內容大小超過指定的矩形範圍，是否將內容大小等比例縮小。</param>
		/// <param name="align">內容在指定的矩形範圍中的對齊方式。</param>
		/// <returns>指定內容在指定範圍中的位置及大小。</returns>
		public static GDIP.Rectangle GetContentBounds(GDIP.Rectangle bounds, GDIP.Size contentSize, bool fitToBounds, GDIP.ContentAlignment align)
		{
			//計算內容大小
			if (fitToBounds)
				contentSize = GetThumbnailSize(bounds.Size, contentSize);

			//計算內容位置
			int x = bounds.Left;
			int y = bounds.Top;
			switch (align)
			{
				case GDIP.ContentAlignment.TopLeft:
					break;
				case GDIP.ContentAlignment.TopCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					break;
				case GDIP.ContentAlignment.TopRight:
					x += (bounds.Width - contentSize.Width);
					break;
				case GDIP.ContentAlignment.MiddleLeft:
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.MiddleCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.MiddleRight:
					x += (bounds.Width - contentSize.Width);
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.BottomLeft:
					y += (bounds.Height - contentSize.Height);
					break;
				case GDIP.ContentAlignment.BottomCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					y += (bounds.Height - contentSize.Height);
					break;
				case GDIP.ContentAlignment.BottomRight:
					x += (bounds.Width - contentSize.Width);
					y += (bounds.Height - contentSize.Height);
					break;
				default:
					throw new ArgumentException("align");
			}

			//回傳
			return new GDIP.Rectangle(x, y, contentSize.Width, contentSize.Height);
		}
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">指定的內容大小，若內容大小超過指定的矩形範圍則等比例縮小。</param>
		/// <param name="align">內容在指定的矩形範圍中的對齊方式。</param>
		/// <returns>指定內容在指定範圍中的位置及大小。</returns>
		public static GDIP.RectangleF GetContentBounds(GDIP.RectangleF bounds, GDIP.SizeF contentSize, GDIP.ContentAlignment align)
		{
			return GetContentBounds(bounds, contentSize, true, align);
		}
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">指定的內容大小。</param>
		/// <param name="fitToBounds">若內容大小超過指定的矩形範圍，是否將內容大小等比例縮小。</param>
		/// <param name="align">內容在指定的矩形範圍中的對齊方式。</param>
		/// <returns>指定內容在指定範圍中的位置及大小。</returns>
		public static GDIP.RectangleF GetContentBounds(GDIP.RectangleF bounds, GDIP.SizeF contentSize, bool fitToBounds, GDIP.ContentAlignment align)
		{
			//計算內容大小
			if (fitToBounds)
				contentSize = GetThumbnailSize(bounds.Size, contentSize);

			//計算內容位置
			float x = bounds.Left;
			float y = bounds.Top;
			switch (align)
			{
				case GDIP.ContentAlignment.TopLeft:
					break;
				case GDIP.ContentAlignment.TopCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					break;
				case GDIP.ContentAlignment.TopRight:
					x += (bounds.Width - contentSize.Width);
					break;
				case GDIP.ContentAlignment.MiddleLeft:
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.MiddleCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.MiddleRight:
					x += (bounds.Width - contentSize.Width);
					y += (bounds.Height - contentSize.Height) / 2;
					break;
				case GDIP.ContentAlignment.BottomLeft:
					y += (bounds.Height - contentSize.Height);
					break;
				case GDIP.ContentAlignment.BottomCenter:
					x += (bounds.Width - contentSize.Width) / 2;
					y += (bounds.Height - contentSize.Height);
					break;
				case GDIP.ContentAlignment.BottomRight:
					x += (bounds.Width - contentSize.Width);
					y += (bounds.Height - contentSize.Height);
					break;
				default:
					throw new ArgumentException("align");
			}

			//回傳
			return new GDIP.RectangleF(x, y, contentSize.Width, contentSize.Height);
		}
		/*
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">內容大小。</param>
		/// <param name="stretchDirection">內容縮放方向。</param>
		/// <param name="horzAlign">內容的水平對齊方式。</param>
		/// <param name="vertAlign">內容的垂直對齊方式。</param>
		/// <returns>指定大小的內容在指定矩形範圍中的位置及大小。</returns>
		/// <exception cref="ArgumentException">無效的 horzAlign 或 vertAlign。</exception>
		public static Rect GetContentBounds(Rect bounds, Size contentSize, StretchDirection stretchDirection, HorizontalAlignment horzAlign, VerticalAlignment vertAlign)
		{
			//計算內容大小
			if (stretchDirection != 0)
				contentSize = GetRatioStretchSize(bounds.Size, contentSize, stretchDirection);

			//計算水平位置
			double x;
			switch (horzAlign)
			{
				case HorizontalAlignment.Left:
					x = bounds.X;
					break;
				case HorizontalAlignment.Center:
					x = bounds.X + (bounds.Width - contentSize.Width) / 2;
					break;
				case HorizontalAlignment.Right:
					x = bounds.Right - contentSize.Width;
					break;
				case HorizontalAlignment.Stretch:
					if (contentSize.Width < bounds.Width)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
						{
							x = bounds.X;
							contentSize.Width = bounds.Width;
						}
						else
							x = bounds.X + (bounds.Width - contentSize.Width) / 2;
					}
					else if (contentSize.Width > bounds.Width)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly)
						{
							x = bounds.X;
							contentSize.Width = bounds.Width;
						}
						else
							x = bounds.X + (bounds.Width - contentSize.Width) / 2;
					}
					else
						x = bounds.X;
					break;
				default:
					throw new ArgumentException();
			}

			//計算垂直位置
			double y;
			switch (vertAlign)
			{
				case VerticalAlignment.Top:
					y = bounds.Y;
					break;
				case VerticalAlignment.Center:
					y = bounds.Y + (bounds.Height - contentSize.Height) / 2;
					break;
				case VerticalAlignment.Bottom:
					y = bounds.Bottom - contentSize.Height;
					break;
				case VerticalAlignment.Stretch:
					if (contentSize.Height < bounds.Height)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
						{
							y = bounds.Y;
							contentSize.Height = bounds.Height;
						}
						else
							y = bounds.Y + (bounds.Height - contentSize.Height) / 2;
					}
					else if (contentSize.Height > bounds.Height)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly)
						{
							y = bounds.Y;
							contentSize.Height = bounds.Height;
						}
						else
							y = bounds.Y + (bounds.Height - contentSize.Height) / 2;
					}
					else
						y = bounds.Y;
					break;
				default:
					throw new ArgumentException();
			}

			//回傳
			return new Rect(x, y, contentSize.Width, contentSize.Height);
		}
		*/
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">內容大小。</param>
		/// <param name="stretchDirection">內容縮放方向。</param>
		/// <param name="horzAlign">內容的水平對齊方式。</param>
		/// <param name="vertAlign">內容的垂直對齊方式。</param>
		/// <returns>指定大小的內容在指定矩形範圍中的位置及大小。</returns>
		/// <exception cref="ArgumentException">無效的 horzAlign 或 vertAlign。</exception>
		public static Rect<int> GetContentBounds(Rect<int> bounds, Size<int> contentSize, StretchDirection stretchDirection, HorizontalAlignment horzAlign, VerticalAlignment vertAlign)
		{
			return GetContentBounds(bounds.ToDoubleRect(), contentSize.ToDoubleSize(), stretchDirection, horzAlign, vertAlign).ToInt32Rect();
		}
		/// <summary>
		/// 計算指定大小的內容在指定矩形範圍中的位置及大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="contentSize">內容大小。</param>
		/// <param name="stretchDirection">內容縮放方向。</param>
		/// <param name="horzAlign">內容的水平對齊方式。</param>
		/// <param name="vertAlign">內容的垂直對齊方式。</param>
		/// <returns>指定大小的內容在指定矩形範圍中的位置及大小。</returns>
		/// <exception cref="ArgumentException">無效的 horzAlign 或 vertAlign。</exception>
		public static Rect<double> GetContentBounds(Rect<double> bounds, Size<double> contentSize, StretchDirection stretchDirection, HorizontalAlignment horzAlign, VerticalAlignment vertAlign)
		{
			//計算內容大小
			if (stretchDirection != 0)
				contentSize = GetRatioStretchSize(bounds.Size, contentSize, stretchDirection);

			//計算水平位置
			double x;
			switch (horzAlign)
			{
				case HorizontalAlignment.Left:
					x = bounds.Left;
					break;
				case HorizontalAlignment.Center:
					x = bounds.Left + (bounds.Width - contentSize.Width) / 2;
					break;
				case HorizontalAlignment.Right:
					x = (bounds.Left + bounds.Width) - contentSize.Width;
					break;
				case HorizontalAlignment.Stretch:
					if (contentSize.Width < bounds.Width)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
						{
							x = bounds.Left;
							contentSize.Width = bounds.Width;
						}
						else
							x = bounds.Left + (bounds.Width - contentSize.Width) / 2;
					}
					else if (contentSize.Width > bounds.Width)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly)
						{
							x = bounds.Left;
							contentSize.Width = bounds.Width;
						}
						else
							x = bounds.Left + (bounds.Width - contentSize.Width) / 2;
					}
					else
						x = bounds.Left;
					break;
				default:
					throw new ArgumentException();
			}

			//計算垂直位置
			double y;
			switch (vertAlign)
			{
				case VerticalAlignment.Top:
					y = bounds.Top;
					break;
				case VerticalAlignment.Center:
					y = bounds.Top + (bounds.Height - contentSize.Height) / 2;
					break;
				case VerticalAlignment.Bottom:
					y = (bounds.Top + bounds.Height) - contentSize.Height;
					break;
				case VerticalAlignment.Stretch:
					if (contentSize.Height < bounds.Height)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
						{
							y = bounds.Top;
							contentSize.Height = bounds.Height;
						}
						else
							y = bounds.Top + (bounds.Height - contentSize.Height) / 2;
					}
					else if (contentSize.Height > bounds.Height)
					{
						if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly)
						{
							y = bounds.Top;
							contentSize.Height = bounds.Height;
						}
						else
							y = bounds.Top + (bounds.Height - contentSize.Height) / 2;
					}
					else
						y = bounds.Top;
					break;
				default:
					throw new ArgumentException();
			}

			//回傳
			return new Rect<double>(x, y, contentSize.Width, contentSize.Height);
		}
		#endregion
		#region GetRatioStretchSize
		/// <summary>
		/// 計算將指定大小等比例縮小或放大至指定邊界大小後的結果。
		/// </summary>
		/// <param name="bounds">邊界大小。</param>
		/// <param name="contentSize">指定的大小。</param>
		/// <returns>縮小或放大後的大小。</returns>
		public static GDIP.Size GetRatioStretchSize(GDIP.Size bounds, GDIP.Size contentSize)
		{
			//確認
			if (bounds.Width <= 0 || bounds.Height <= 0)
				return new GDIP.Size();
			if (contentSize.Width >= bounds.Width && contentSize.Height >= bounds.Height)
				return GetThumbnailSize(bounds, contentSize);

			//計算比例
			double ratioX = (bounds.Width / (double)contentSize.Width);
			double ratioY = (bounds.Height / (double)contentSize.Height);
			double ratio = (ratioX <= ratioY ? ratioX : ratioY);

			//回傳
			return new GDIP.Size((int)(contentSize.Width * ratio + 0.5), (int)(contentSize.Height * ratio + 0.5));
		}
		/*
		/// <summary>
		/// 計算將指定大小等比例縮小或放大至指定邊界大小後的結果。
		/// </summary>
		/// <param name="bounds">邊界大小。</param>
		/// <param name="contentSize">指定的大小。</param>
		/// <param name="stretchDirection">縮放方向。</param>
		/// <returns>縮小或放大後的大小。</returns>
		public static Int32Size GetRatioStretchSize(Int32Size bounds, Int32Size contentSize, StretchDirection stretchDirection)
		{
			Size result = GetRatioStretchSize(new Size(bounds.Width, bounds.Height), new Size(contentSize.Width, contentSize.Height), stretchDirection);
			return new Int32Size((int)result.Width, (int)result.Height);
		}
		/// <summary>
		/// 計算將指定大小等比例縮小或放大至指定邊界大小後的結果。
		/// </summary>
		/// <param name="bounds">邊界大小。</param>
		/// <param name="contentSize">指定的大小。</param>
		/// <param name="stretchDirection">縮放方向。</param>
		/// <returns>縮小或放大後的大小。</returns>
		public static Size GetRatioStretchSize(Size bounds, Size contentSize, StretchDirection stretchDirection)
		{
			//確認
			if (bounds.Width <= 0 || bounds.Height <= 0)
				return new System.Windows.Size();

			//計算比例
			double ratioX = (bounds.Width / contentSize.Width);
			double ratioY = (bounds.Height / contentSize.Height);
			double ratio;
			if ((stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly) && (ratioX <= 1 || ratioY <= 1))
				ratio = (ratioX <= ratioY ? ratioX : ratioY);
			else if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
				ratio = 1 / (ratioX >= ratioY ? ratioX : ratioY);
			else
				return contentSize;

			//回傳
			return new Size(contentSize.Width * ratio, contentSize.Height * ratio);
		}
		*/
		/// <summary>
		/// 計算將指定大小等比例縮小或放大至指定邊界大小後的結果。
		/// </summary>
		/// <param name="bounds">邊界大小。</param>
		/// <param name="contentSize">指定的大小。</param>
		/// <param name="stretchDirection">縮放方向。</param>
		/// <returns>縮小或放大後的大小。</returns>
		public static Size<double> GetRatioStretchSize(Size<double> bounds, Size<double> contentSize, StretchDirection stretchDirection)
		{
			//確認
			if (bounds.Width <= 0 || bounds.Height <= 0)
				return new Size<double>();

			//計算比例
			double ratioX = (bounds.Width / contentSize.Width);
			double ratioY = (bounds.Height / contentSize.Height);
			double ratio;
			if ((stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly) && (ratioX <= 1 || ratioY <= 1))
				ratio = (ratioX <= ratioY ? ratioX : ratioY);
			else if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
				ratio = 1 / (ratioX >= ratioY ? ratioX : ratioY);
			else
				return contentSize;

			//回傳
			return new Size<double>(contentSize.Width * ratio, contentSize.Height * ratio);
		}
		/// <summary>
		/// 計算將指定大小等比例縮小或放大至指定邊界大小後的結果。
		/// </summary>
		/// <param name="bounds">邊界大小。</param>
		/// <param name="contentSize">指定的大小。</param>
		/// <param name="stretchDirection">縮放方向。</param>
		/// <returns>縮小或放大後的大小。</returns>
		public static Size<int> GetRatioStretchSize(Size<int> bounds, Size<int> contentSize, StretchDirection stretchDirection)
		{
			//確認
			if (bounds.Width <= 0 || bounds.Height <= 0)
				return new Size<int>();

			//計算比例
			double ratioX = (bounds.Width / (double)contentSize.Width);
			double ratioY = (bounds.Height / (double)contentSize.Height);
			double ratio;
			if ((stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.DownOnly) && (ratioX <= 1 || ratioY <= 1))
				ratio = (ratioX <= ratioY ? ratioX : ratioY);
			else if (stretchDirection == StretchDirection.Both || stretchDirection == StretchDirection.UpOnly)
				ratio = 1 / (ratioX >= ratioY ? ratioX : ratioY);
			else
				return contentSize;

			//回傳
			return new Size<int>((int)(contentSize.Width * ratio), (int)(contentSize.Height * ratio));
		}
		#endregion
		#region GetThumbnailSize
		/// <summary>
		/// 計算將指定大小等比例縮小至指定矩形範圍可以容納的大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="originalSize">原始大小。</param>
		/// <returns>等比例縮小後的大小，若原始大小小於指定範圍則不會縮小。</returns>
		public static GDIP.Size GetThumbnailSize(GDIP.Size bounds, GDIP.Size originalSize)
		{
			//計算大小
			GDIP.SizeF sizeF = GetThumbnailSize((GDIP.SizeF)bounds, originalSize);
			GDIP.Size size = new GDIP.Size((int)(sizeF.Width + 0.5f), (int)(sizeF.Height + 0.5f));

			//修正大小
			if (size.Width == 0)
				size.Width = 1;
			if (size.Height == 0)
				size.Height = 1;

			//回傳
			return size;
		}
		/// <summary>
		/// 計算將指定大小等比例縮小至指定矩形範圍可以容納的大小。
		/// </summary>
		/// <param name="bounds">指定的矩形範圍。</param>
		/// <param name="originalSize">原始大小。</param>
		/// <returns>等比例縮小後的大小，若原始大小小於指定範圍則不會縮小。</returns>
		public static GDIP.SizeF GetThumbnailSize(GDIP.SizeF bounds, GDIP.SizeF originalSize)
		{
			//記算大小
			if (bounds.IsEmpty)
				originalSize = new GDIP.SizeF();
			else
			{
				//取得比例
				float ratioX = ((float)bounds.Width) / originalSize.Width;
				float ratioY = ((float)bounds.Height) / originalSize.Height;

				//重新計算內容大小
				if (ratioX < 1)
				{
					if (ratioY < 1)
					{
						if (ratioX <= ratioY)
						{
							originalSize.Width = originalSize.Width * ratioX;
							originalSize.Height = originalSize.Height * ratioX;
						}
						else
						{
							originalSize.Width = originalSize.Width * ratioY;
							originalSize.Height = originalSize.Height * ratioY;
						}
					}
					else
					{
						originalSize.Width = bounds.Width;
						originalSize.Height = originalSize.Height * ratioX;
					}
				}
				else if (ratioY < 1)
				{
					originalSize.Width = originalSize.Width * ratioY;
					originalSize.Height = bounds.Height;
				}
			}

			//回傳
			return originalSize;
		}
		#endregion
	}
	#region HorizontalAlignment
	/// <summary>
	/// 表示水平對齊方式。
	/// </summary>
	public enum HorizontalAlignment
	{
		/// <summary>
		/// 向左對齊。
		/// </summary>
		Left,
		/// <summary>
		/// 向右對齊。
		/// </summary>
		Right,
		/// <summary>
		/// 置中。
		/// </summary>
		Center,
		/// <summary>
		/// 水平延伸。
		/// </summary>
		Stretch,
	}
	#endregion
	#region VerticalAlignment
	/// <summary>
	/// 表示垂直對齊方式。
	/// </summary>
	public enum VerticalAlignment
	{
		/// <summary>
		/// 向上對齊。
		/// </summary>
		Top,
		/// <summary>
		/// 向下對齊。
		/// </summary>
		Bottom,
		/// <summary>
		/// 置中。
		/// </summary>
		Center,
		/// <summary>
		/// 垂直延伸。
		/// </summary>
		Stretch,
	}
	#endregion
	#region StretchDirection
	/// <summary>
	/// 表示延伸的方向。
	/// </summary>
	public enum StretchDirection
	{
		/// <summary>
		/// 允許放大及縮小。
		/// </summary>
		Both,
		/// <summary>
		/// 只允許縮小。
		/// </summary>
		DownOnly,
		/// <summary>
		/// 只允許放大。
		/// </summary>
		UpOnly,
	}
	#endregion
}